
//#include <conio.h>
//#include <i86.h>
#include <string.h>
#include <strings.h>
#include "testcase.h"


/*
 * All commands will be issued simultaneously.
 * Each operation includes erase, write and read.
 *
 * CmdNum     : the number of commands to be issued simultaneously.
 * MaxLoopNum : the total loop number of erase, write and read to the whole disk C.
 * Length     : the length of each operation, in units of byte.
 */
STATUS TestCaseAsyncRWDiskAB( PAHCI_EXT AhciExt, UCHAR DiskIndex, UCHAR CmdNum, ULONG MaxLoopNum, ULONG Length )
{
    CMD_INFO cmdInfo[ 32 ];
    UCHAR completedCmdNum;
    ULONG val;
    ULONG i;
    STATUS ret;

    ULONG  maxTotalByteNumPerCmd; // the total number of bytes to read and write in one command per loop.
    ULONG  memBASw;
    ULONG  stageSize;   // byte or sector
    UCHAR  shiftBitNum;
    ULONG  memAddr;
/*
Bytes    |   Sector
---------+-----------
Length   |   stageSize
---------+-----------



*/
    i   = 0;
    // wait until
    // either PxCI is 0, which means all commands are idle.
    // or timeout.
    do{
        val = *AhciExt->PxCIRegAddress;
        if ( i++ > 5000 )
        {
            LogMsg( COMP_HW, DBG_ERROR, "There are some command busy. PxCI %08X \n", val);
            return UNSUCCESSFUL;
        }
    }while( val != 0 );

    if ( DiskIndex == SUB_DISK_A )
    {
        memBASw    = AhciExt->DiskAAddrSw;
        stageSize   = Length;
        maxTotalByteNumPerCmd = DISK_A_SIZE / CmdNum;
        shiftBitNum     = 0;
    }
    else
    {
        memBASw    = AhciExt->DiskBAddrSw;
        stageSize   = Length / SECTOR_SZ;
        maxTotalByteNumPerCmd = DISK_B_SIZE / CmdNum;
        shiftBitNum     = 9; // 512
    }

    for( i = 0; i < CmdNum; i++)
    {
        cmdInfo[ i ].StartOffset    = i * ( maxTotalByteNumPerCmd >> shiftBitNum );
        cmdInfo[ i ].CurrentOffset  = cmdInfo[ i ].StartOffset;
        cmdInfo[ i ].LoopNum        = 0;
    }
    completedCmdNum = 0;
    while( completedCmdNum != ( ( 1 << CmdNum ) - 1 ) )
    {
        val = ~(*AhciExt->PxCIRegAddress);
        // ffs: find out the first set bit in order to find the first idle command.
        // 1: the command has been completed and is idle right now.
        // 0: the command is busy at the moment.
        i = ffs ( val );
        while ( ( i > 0 ) && ( i <= CmdNum ) )
        {
            switch( cmdInfo[ i - 1 ].State )
            {
            case CMD_WRITING:
                // the just completed command is write operation
                // issue an read operation at the CurrentOffset.

                cmdInfo[ i - 1 ].State = CMD_READING;
                break;
            case CMD_READING:
                {
                    ret = CheckPayload( 
                        (PUCHAR)( memBASw + ( cmdInfo[ i - 1 ].CurrentOffset << shiftBitNum ) ),
                        Length,
                        &cmdInfo[ i - 1 ].MagicNum,
                        &cmdInfo[ i - 1 ].Crc
                        );
                    LogMsg( COMP_HW, DBG_WARNING, "CMD %d OFFSET %08X  LEN %d Actual %d CRC %s\n",
                        ( i - 1 ), 
                        (PUCHAR)( memBASw + ( cmdInfo[ i - 1 ].CurrentOffset << shiftBitNum ) ),
                        Length,
                        AhciExt->HostCmdTable[ i - 1 ].FinishCnt[ DiskIndex ],
                        ( ( SUCCESS == ret ) ? "OK" : "ERR")
                        );
                    cmdInfo[ i - 1 ].CurrentOffset += stageSize;
                    if ( ( ( cmdInfo[ i - 1 ].CurrentOffset  - cmdInfo[ i - 1 ].StartOffset ) << shiftBitNum )
                        >= maxTotalByteNumPerCmd )
                    {
                        // return to StartOffset
                        cmdInfo[ i - 1 ].CurrentOffset = cmdInfo[ i - 1 ].StartOffset;
                        cmdInfo[ i - 1 ].LoopNum ++;
                        if ( cmdInfo[ i - 1 ].LoopNum >= MaxLoopNum )
                        {
                            completedCmdNum |= ( 1 << ( i - 1 ) );
                            cmdInfo[ i - 1 ].State = CMD_COMPLETED;
                            break;
                        }
                    }
                }
                // NOT break, will issue new write operation just as CMD_IDLE.
            case CMD_IDLE:
                // issue a new write/read operations.
                // increase CMD_INFO
                memAddr = memBASw + ( cmdInfo[ i - 1 ].CurrentOffset << shiftBitNum);
                cmdInfo[ i - 1 ].MagicNum = ( cmdInfo[ i - 1 ].LoopNum << 16 ) + ( memAddr >> 16 );
                cmdInfo->Crc =  FillPayload(
                    (PUCHAR)memAddr,
                    Length,
                    &cmdInfo[ i - 1 ].MagicNum
                    );
                memset( (PVOID)&AhciExt->HostCmdTable[ i - 1 ], 0, sizeof( HOST_CMD ) );
                cmdInfo[ i - 1 ].State = CMD_WRITING;
                break;
            case CMD_COMPLETED:
                    LogMsg( COMP_HW, DBG_WARNING, "CMD %d OFFSET %08X  LEN %d Actual %d CRC %s\n",
                        ( i - 1 ), 
                        (PUCHAR)( memBASw + ( cmdInfo[ i - 1 ].CurrentOffset << shiftBitNum ) ),
                        Length,
                        AhciExt->HostCmdTable[ i - 1 ].FinishCnt[ DiskIndex ],
                        ( ( SUCCESS == ret ) ? "OK" : "ERR")
                        );
                break;

            }
            if ( ( cmdInfo[ i - 1 ].State == CMD_READING )
                || ( cmdInfo[ i - 1 ].State == CMD_WRITING ) )
            {
                BuildSubDiskCmd( &AhciExt->HostCmdTable[ i - 1 ],
                    DiskIndex,
                    ( ( cmdInfo[ i - 1 ].State == CMD_WRITING ) ? HOST_REQ_WRITE : HOST_REQ_READ ),
                    cmdInfo[ i - 1 ].CurrentOffset,
                    stageSize
                     );
                *AhciExt->PxCIRegAddress = ( 0x1 << ( i - 1 ) );
            }

            // mask current bit and skip it.
            val &= ( ~( 1 << ( i - 1 ) ) );
            i = ffs ( val );
        }
    }
    return SUCCESS;
}

STATUS TestCaseSyncEWRDiskCOnce( PAHCI_EXT AhciExt, ULONG Offset, ULONG Length)
{
    STATUS ret;
    ULONG crc;
    ret = SyncRWEDisk( AhciExt, SUB_DISK_C, HOST_REQ_ERASE, 0, Offset, Length, NULL );
    if ( SUCCESS != ret )
    {
        LogMsg( COMP_HW, DBG_ERROR, "Failed to erase disk C error code %d at %08X length %d\n", ret, Offset, Length );
        return ret;
    }
    ret = SyncRWEDisk( AhciExt, SUB_DISK_C, HOST_REQ_WRITE, 0, Offset, Length, &crc );
    if ( SUCCESS != ret )
    {
        LogMsg( COMP_HW, DBG_ERROR, "Failed to write disk C error code %d at %08X length %d\n", ret, Offset, Length );
        return ret;
    }
    ret = SyncRWEDisk( AhciExt, SUB_DISK_C, HOST_REQ_READ, 0, Offset, Length, &crc );
    if ( SUCCESS != ret )
    {
        LogMsg( COMP_HW, DBG_ERROR, "Failed to read  disk C error code %d at %08X length %d\n", ret, Offset, Length );
        return ret;
    }
    LogMsg( COMP_HW, DBG_WARNING, "Succeed to E/W/R  disk C at %08X length %d\n", ret, Offset, Length );
    return SUCCESS;
}
/*
 * All commands will be issued simultaneously.
 * Each operation includes erase, write and read.
 *
 * CmdNum : how many command will be issued simultaneously.
 * LoopNum: the total loop number of erase, write and read to the whole disk C.
 * Length : the length of each operation, in units of byte.
 */
STATUS TestCaseAsyncEWRDiskC( PAHCI_EXT AhciExt, UCHAR CmdNum, ULONG LoopNum, ULONG Length)
{
    CMD_INFO_FLASH  cmdInfo[ 32 ];
    COMMON_CMD_INFO commonInfo;
    ULONG val;
    ULONG i;
    STATUS ret;

    i   = 0;
    // wait until
    // either PxCI is 0, which means all commands are idle.
    // or timeout.
    do{
        val = *AhciExt->PxCIRegAddress;
        if ( i++ > 5000 )
        {
            LogMsg( COMP_HW, DBG_ERROR, "There are some command busy. PxCI %08X \n", val);
            return UNSUCCESSFUL;
        }
    }while( val != 0 );

    commonInfo.MemBASw          = AhciExt->DiskCAddrSw;
    commonInfo.StageLength      = Length / SECTOR_SZ;
    commonInfo.MaxNumPerLoop    = DISK_C_SIZE / CmdNum / Length;
    commonInfo.MaxLoopNum       = LoopNum;
    commonInfo.CompletedBitmap  = 0;


    for( i = 0; i < CmdNum; i++)
    {
        cmdInfo[ i ].Tag            = (UCHAR)i;
        cmdInfo[ i ].StartOffset    = ( DISK_C_SIZE >> SECTOR_SZ_BIT ) / CmdNum * i;
        cmdInfo[ i ].CurrentIndex   = 0;
        cmdInfo[ i ].LoopNum        = 0;
        cmdInfo[ i ].HostCmd        = &AhciExt->HostCmdTable[ i ];
        cmdInfo[ i ].pHandler       = AsyncEraseFlash;
        cmdInfo[ i ].State          = CMD_IDLE;
    }
    while( commonInfo.CompletedBitmap != ( ( 1 << CmdNum ) - 1 ) )
    {
        val = ~(*AhciExt->PxCIRegAddress);
        // ffs: find out the first set bit in order to find the first idle command.
        // 1: the command has been completed and is idle right now.
        // 0: the command is busy at the moment.
        i = ffs ( val );
        while ( ( i > 0 ) && ( i <= CmdNum ) )
        {
            ret = cmdInfo[ i - 1 ].pHandler( &cmdInfo[ i - 1 ], &commonInfo );
            if ( SUCCESS == ret )
            {
                *AhciExt->PxCIRegAddress = ( 0x1 << ( i - 1 ) );
            }
            // mask current bit and skip it.
            val &= ( ~( 1 << ( i - 1 ) ) );
            i = ffs ( val );
        }
    }
    return SUCCESS;
}

STATUS AsyncEraseFlash( PCMD_INFO_FLASH CmdInfoFlash, PCOMMON_CMD_INFO CommonInfo )
{
    ULONG offset;

    offset                  = CmdInfoFlash->StartOffset + ( CmdInfoFlash->CurrentIndex * CommonInfo->StageLength );
    CmdInfoFlash->State     = CMD_IDLE;
    CmdInfoFlash->pHandler  = AsyncWriteFlash;
    memset( (PVOID)CmdInfoFlash->HostCmd, 0, sizeof( HOST_CMD ) );
    BuildSubDiskCmd( CmdInfoFlash->HostCmd,
        SUB_DISK_C,
        HOST_REQ_ERASE,
        offset,
        CommonInfo->StageLength
        );
    return SUCCESS;
}
STATUS AsyncWriteFlash( PCMD_INFO_FLASH CmdInfoFlash, PCOMMON_CMD_INFO CommonInfo )
{
    ULONG offset;
    ULONG memAddr;
    STATUS ret;

    offset                  = CmdInfoFlash->StartOffset + ( CmdInfoFlash->CurrentIndex * CommonInfo->StageLength );
    memAddr                 = CommonInfo->MemBASw + ( offset << SECTOR_SZ_BIT );
    CmdInfoFlash->MagicNum  = ( CmdInfoFlash->LoopNum << 16 ) + ( memAddr >> 16 );
    CmdInfoFlash->State     = CMD_WRITING;
    CmdInfoFlash->pHandler  = AsyncReadFlash;
    CmdInfoFlash->Crc       = FillPayload(
        (PUCHAR)memAddr,
        ( CommonInfo->StageLength << SECTOR_SZ_BIT ),
        &CmdInfoFlash->MagicNum
        );

    memset( (PVOID)CmdInfoFlash->HostCmd, 0, sizeof( HOST_CMD ) );
    ret = BuildSubDiskCmd( CmdInfoFlash->HostCmd,
        SUB_DISK_C,
        HOST_REQ_WRITE,
        offset,
        CommonInfo->StageLength
        );
    return ret;
}
STATUS AsyncReadFlash( PCMD_INFO_FLASH CmdInfoFlash, PCOMMON_CMD_INFO CommonInfo )
{
    ULONG offset;
    STATUS ret;

    offset                  = CmdInfoFlash->StartOffset + ( CmdInfoFlash->CurrentIndex * CommonInfo->StageLength );
    CmdInfoFlash->State     = CMD_READING;
    CmdInfoFlash->pHandler  = CheckDataForReadFlash;

    ret = BuildSubDiskCmd( CmdInfoFlash->HostCmd,
        SUB_DISK_C,
        HOST_REQ_READ,
        offset,
        CommonInfo->StageLength
        );
    return ret;
}
/*
 * Advance state from CheckData to Write or Erase.
 */
STATUS CheckDataForReadFlash(PCMD_INFO_FLASH CmdInfoFlash, PCOMMON_CMD_INFO CommonInfo )
{
    ULONG offset;
    ULONG memAddr;
    STATUS ret;

    offset  = CmdInfoFlash->StartOffset + ( CmdInfoFlash->CurrentIndex * CommonInfo->StageLength );
    memAddr = CommonInfo->MemBASw + ( offset << 9 );
    ret = CheckPayload( 
        (PUCHAR)memAddr,
        ( CommonInfo->StageLength << SECTOR_SZ_BIT ),
        &CmdInfoFlash->MagicNum,
        &CmdInfoFlash->Crc
        );
    LogMsg( COMP_HW, DBG_WARNING, "CMD %d OFFSET %08X(Sec)  LEN %d(Sec) Actual %d CRC %s\n",
        CmdInfoFlash->Tag, 
        offset,
        ( CommonInfo->StageLength << SECTOR_SZ_BIT ),
        CmdInfoFlash->HostCmd->FinishCnt[ SUB_DISK_C ],
        ( ( SUCCESS == ret ) ? "OK" : "ERR")
        );

    CmdInfoFlash->CurrentIndex++;
    if ( CmdInfoFlash->CurrentIndex >= CommonInfo->MaxNumPerLoop )
    {
        CmdInfoFlash->CurrentIndex = 0;
        CmdInfoFlash->LoopNum++;
        if ( CmdInfoFlash->LoopNum >= CommonInfo->MaxLoopNum )
        {
            CommonInfo->CompletedBitmap |= ( 1 << CmdInfoFlash->Tag );
            CmdInfoFlash->pHandler = NullFunction;
        }
        else
        {
            CmdInfoFlash->pHandler = AsyncEraseFlash;
        }
    }
    else
    {
        CmdInfoFlash->pHandler = AsyncWriteFlash;
    }
    ret = CmdInfoFlash->pHandler( CmdInfoFlash, CommonInfo );

    return ret;
}
STATUS NullFunction( PCMD_INFO_FLASH CmdInfoFlash, PCOMMON_CMD_INFO CommonInfo )
{
    return LOOP_COMPLETED;
}

